package com.iceclean.controller;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.iceclean.po.*;
import com.iceclean.service.PostService;
import com.iceclean.service.ResourceService;
import com.iceclean.serviceImpl.PostServiceImpl;
import com.iceclean.serviceImpl.ResourceServiceImpl;
import com.iceclean.util.ServiceAgent;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.UUID;

/**
 * @author : Ice'Clean
 * @date : 2021-05-07
 *
 * 接收所有与贴子操作有关的请求
 */
@WebServlet(name = "PostServlet", value = "/PostServlet")
public class PostServlet extends BaseServlet {

    public static final String NULL = "null";

    /**
     * 贴子服务的动态代理
     * 资源服务的动态代理
     */
    PostService postService = (PostService) new ServiceAgent(new PostServiceImpl()).getAgent();
    ResourceService resourceService = (ResourceService) new ServiceAgent(new ResourceServiceImpl()).getAgent();


    /**
     * 创建贴子
     * 如果有传入设置为公告贴
     * 那么需要设置当前贴子的管理员 id
     */
    public void createPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 获取贴子基本信息
        int postBarId = Integer.parseInt(request.getParameter("postBarId"));
        int userId = Integer.parseInt(request.getParameter("userId"));
        String postName = request.getParameter("postName");
        String postContent = request.getParameter("postContent");

        // 获取可能存在的管理员 id
        String postAdminId = request.getParameter("postAdminId");

        // 判断是否为公告贴并创建新贴子
        int postId;
        if (postAdminId != null) {
            // 是则创建公告贴
            postId = postService.createPost(postBarId, userId, postName, postContent, Integer.parseInt(postAdminId));
        } else {
            // 不是则创建普通贴（管理员 id 传入 -1）
            postId = postService.createPost(postBarId, userId, postName, postContent, -1);
        }

        // 将贴吧实体发送给给前端
        Post post = postService.findPostById(postId);

        ObjectMapper mapper = new ObjectMapper();
        HttpSession session = request.getSession();
        session.setAttribute("post", post);

        response.setContentType("text/html;charset=utf-8");
        response.getWriter().write(mapper.writeValueAsString(post));
    }

    /**
     * 删除贴子
     */
    public void deletePost(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 获取要删除的贴子 id
        int postId = Integer.parseInt(request.getParameter("postId"));

        // 执行删除
        boolean delete = postService.deletePost(postId);

        // 返回给前端
        response.setContentType("text/html;charset=utf-8");
        response.getWriter().write("" + delete);
    }

    /**
     * 恢复贴子
     */
    public void recoverPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 获取要恢复的贴子的 id
        int postId = Integer.parseInt(request.getParameter("postId"));

        // 执行恢复
        boolean recover = postService.recoverPost(postId);

        // 返回给前端
        response.setContentType("text/html;charset=utf-8");
        response.getWriter().write("" + recover);
    }

    /**
     * 创建贴子回复
     * 通过是否传入根回复 id 判断是根回复还是子回复
     */
    public void createPostComment(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 获取到贴子id，用户id以及回复的内容
        int postId = Integer.parseInt(request.getParameter("postId"));
        int userId = Integer.parseInt(request.getParameter("userId"));
        String commentContent = request.getParameter("commentContent");

        // 获取得到回复的根回复 id，使用 String 接收可能的空值
        String rootCommentId = request.getParameter("rootCommentId");
        // 同时还需要接收该评论上传全部图片资源的唯一标识，用来去掉中途不想要的图片，在服务器端删除图片
        String resourceIdentify = request.getParameter("resourceIdentify");

        // 获取到当前绑定的贴子，同步更新数据
        Post post = (Post) request.getSession().getAttribute("post");

        // 通过根回复 id 判断是根回复还是子回复
        PostComment postComment;
        if (NULL.equals(rootCommentId)) {
            // 为空则是根回复，创建根回复对象
            postComment = postService.createPostComment(postId, userId, commentContent);

            // 是根回复的话，需要扫面内容中存在的图片资源，并将废弃的资源删去，同时给图片资源设置上评论的 id 实现绑定
            resourceService.checkContentResource(commentContent, resourceIdentify, postComment.getCommentId());

            // 更新楼层数
            post.setPostFloor(post.getPostFloor() + 1);
        } else {
            // 不为空则是子回复，创建子回复对象
            postComment = postService.createPostComment(postId, userId, Integer.parseInt(rootCommentId), commentContent);
        }

        // 更新评论数
        post.setPostComment(post.getPostComment() + 1);
        request.getSession().setAttribute("post", post);

        // 将贴吧实体发送给给前端
        ObjectMapper mapper = new ObjectMapper();
        response.setContentType("text/html;charset=utf-8");
        response.getWriter().write(mapper.writeValueAsString(postComment));
    }

    /**
     * 删除贴子回复
     * 是子回复的话直接删除，根回复的话会连同所有子回复一起删除（Service层实现）
     */
    public void deletePostComment(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 获取要删除的回复 id
        int postCommentId = Integer.parseInt(request.getParameter("postCommentId"));

        // 执行删除操作（先删除回复所持有的资源，再删除回复本身）
        boolean delete = postService.deletePostComment(postCommentId);

        // 返回给前端
        response.setContentType("text/html;charset=utf-8");
        response.getWriter().write("" + delete);
    }

    /**
     * 获取 session 中的贴子，即绑定了的贴子
     */
    public void getPostBySession(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 从 session 中取出存放的贴子对象
        Post post =  (Post) request.getSession().getAttribute("post");
        ObjectMapper mapper = new ObjectMapper();
        response.setContentType("text/html;charset=utf-8");
        response.getWriter().write(mapper.writeValueAsString(post));
    }

    /**
     * 通过 id 获取贴子
     * 在贴子被点击时，将贴子保存到 session 中，同时跳转到该贴子
     * 没有被点击时，返回贴子对象
     */
    public void getPostById(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setCharacterEncoding("utf-8");

        // 贴子列表中的超链接发过来的 Get 请求，携带了贴子 id
        int postId = Integer.parseInt(request.getParameter("postId"));
        boolean isClick = Boolean.parseBoolean(request.getParameter("isClick"));

        Post post = postService.findPostById(postId);
        System.out.println(post.getPostName());

        if (isClick) {
            // 当执行的是点击事件时，将该贴子保存到 session 中并跳转到该贴子
            // 将获取到的贴吧对象存放到 session 中
            HttpSession session = request.getSession();
            session.setAttribute("post", post);

            // 然后跳转到贴吧页面中，贴吧页面会自动发送一个 post 请求（就是下面的 doPost），获取到刚刚存进去的贴子对象
            response.sendRedirect("html/postContent.html");
        } else {
            // 不是的话则放回该贴子对象
            ObjectMapper mapper = new ObjectMapper();
            response.getWriter().write(mapper.writeValueAsString(post));
        }

    }

    /**
     * 按页数获取贴子
     * （1）有传进来用户 id 的，返回的贴子为该用户创建的
     * （2）有传进来搜索 search 的，返回的贴子是包含该内容的
     * （3）有传进来删除标识的，0 标识查找正常的贴子，1 标识查找被删除的贴子
     * （1）和（2）都默认查找正常的贴子
     * 其中（2）和（3）都有排序功能
     * 分别是：sortClass 排序种类（时间、回复），sortWay 排序方式（升序，降序）
     */
    public void getAllPostByPage(HttpServletRequest request, HttpServletResponse response) throws IOException {
        request.setCharacterEncoding("utf-8");

        // 接收可能有的用户 id 、搜索 search 和删除标识 postDelete
        String userId = request.getParameter("userId");
        String search = request.getParameter("search");
        String postDelete = request.getParameter("postDelete");

        // 按页数获取贴子
        // 获取开始和结束的下标
        int page = Integer.parseInt(request.getParameter("page"));
        int num = Integer.parseInt(request.getParameter("num"));

        // 获取排序的方式和种类
        String sortClass = request.getParameter("sortClass");
        String sortWay = request.getParameter("sortWay");

        List<Post> posts;

        if (userId != null) {
            // 有用户 id 的，则获取用户发布的所有贴子
            posts = postService.findUserPostsByPage(Integer.parseInt(userId), page, num);
        } else if (search != null) {
            // 有搜索 search 的，进行模糊搜索所属贴吧的贴子
            // 获取所属的贴吧 id
            int postBarId = Integer.parseInt(request.getParameter("postBarId"));
            posts = postService.searchPostByPage(postBarId, search, page, num, sortClass, sortWay);
        } else {
            // 否则即为只看正常的贴子或只看被删除的贴子
            // 获取所属的贴吧 id
            int postBarId = Integer.parseInt(request.getParameter("postBarId"));
            posts = postService.findPostsByPage(postBarId, page, num, Integer.parseInt(postDelete), sortClass, sortWay);
        }

        response.setContentType("text/html;charset=utf-8");
        ObjectMapper mapper = new ObjectMapper();
        response.getWriter().write(mapper.writeValueAsString(posts));
    }

    /**
     * 获取到贴吧内所有被删除贴子的数量
     */
    public void getAllDeletePostForNumber(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 获取贴吧 id
        int postBarId = Integer.parseInt(request.getParameter("postBarId"));

        // 执行查询数量操作，并返回给前端
        int number = postService.getAllDeletePostForNumber(postBarId);
        response.setContentType("text/html;charset=utf-8");
        response.getWriter().write(""+number);
    }

    /**
     * 获取按搜索字段搜索出来的贴子总数
     * 只在搜索按钮被按下时执行
     */
    public void getAllSearchForNumber(HttpServletRequest request, HttpServletResponse response) throws IOException {
        request.setCharacterEncoding("utf-8");

        // 获取所属贴吧和对应的搜索字段
        int postBarId = Integer.parseInt(request.getParameter("postBarId"));
        String search = request.getParameter("search");

        int number = postService.getAllSearchForNumber(postBarId, search);
        response.setContentType("text/html;charset=utf-8");
        response.getWriter().write(""+number);
    }

    /**
     * 按页数获取贴子的回复
     * 通过传进来的根评论 id 判断要获取的是否是某个评论的子评论
     * 如果根评论 id 为 null，则索说明获取的为贴子的评论
     * 不为 null 则是获取该贴子的子评论
     */
    public void getAllPostCommentByPage(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 获取到对应贴子的 id, 页数以及每页的数量
        int postId = Integer.parseInt(request.getParameter("postId"));
        int page = Integer.parseInt(request.getParameter("page"));
        int num = Integer.parseInt(request.getParameter("num"));

        // 获取得到回复的根回复 id，使用 String 接收可能的空值
        String rootCommentId = request.getParameter("rootCommentId");

        // 通过根回复 id 判断是根回复还是子回复
        List<PostComment> postCommentList;
        if (NULL.equals(rootCommentId)) {
            // 为空则是根回复，按页数获取根回复对象
            postCommentList = postService.findPostCommentByPage(postId, page, num);
        } else {
            // 不为空则是子回复，按页数以该根回复 id 获取子回复对象
            postCommentList = postService.findPostCommentByPage(postId, Integer.parseInt(rootCommentId), page, num);
        }

        // 最后返回给前端
        response.setContentType("text/html;charset=utf-8");
        ObjectMapper mapper = new ObjectMapper();
        response.getWriter().write(mapper.writeValueAsString(postCommentList));
    }

    /**
     * 获取浏览记录的总数
     */
    public void getAllBrowsingForNumber(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 获取绑定的用用户
        User user = (User) request.getSession().getAttribute("user");

        // 获取历史浏览总数
        int number = postService.getNumberOfAllBrowsing(user.getUserId());

        response.setContentType("text/html;charset=utf-8");
        response.getWriter().write(""+number);
    }

    /**
     * 按页数获取浏览记录
     */
    public void getAllBrowsingByPage(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 获取绑定的用户
        User user = (User) request.getSession().getAttribute("user");

        // 按页数获取贴子
        // 获取开始和结束的下标
        int page = Integer.parseInt(request.getParameter("page"));
        int num = Integer.parseInt(request.getParameter("num"));

        List<Post> postList = postService.findBrowsingByPage(user.getUserId(), page, num);

        response.setContentType("text/html;charset=utf-8");
        ObjectMapper mapper = new ObjectMapper();
        response.getWriter().write(mapper.writeValueAsString(postList));
    }

    /**
     * 清空用户的历史浏览记录
     */
    public void cleanAllBrowsing(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 获取绑定的用户，然后删除掉所有的浏览记录
        User user = (User) request.getSession().getAttribute("user");
        postService.deleteUserBrowsing(user.getUserId());
    }

    /**
     * 增加贴子的浏览（包括贴子阅读量增加、用户历史浏览记录）
     */
    public void addPostBrowsing(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 获取相关的贴子和用户（已绑定的）
        Post post = (Post) request.getSession().getAttribute("post");
        User user = (User) request.getSession().getAttribute("user");

        postService.addPostBrowsing(post.getPostId(), user.getUserId());

        // 同步贴子阅读量到前端
        post.setPostBrowsing(post.getPostBrowsing() + 1);
        request.getSession().setAttribute("post", post);
    }

    /**
     * 改变贴子的点赞数（点赞 / 取消点赞）
     */
    public void changePostLiked(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 获取点赞相关的贴子和用户（已绑定的）
        Post post = (Post) request.getSession().getAttribute("post");
        User user = (User) request.getSession().getAttribute("user");

        // 执行点赞操作
        PostLiked postLiked = postService.changePostLiked(post.getPostId(), user.getUserId());

        // 判断是新建点赞还是取消点赞，然后返回给前端
        response.setContentType("text/html;charset=utf-8");
        if (postLiked == null) {
            // 为 null 说明该操作为取消点赞
            post.setPostLiked(post.getPostLiked() - 1);
            request.getSession().setAttribute("post", post);
            response.getWriter().write("cancel");
        } else {
            // 否则为新建点赞
            post.setPostLiked(post.getPostLiked() + 1);
            request.getSession().setAttribute("post", post);
            response.getWriter().write("create");
        }
    }

    /**
     * 改变贴子的收藏数（收藏 / 取消收藏）
     */
    public void changePostCollected(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 获取收藏相关的贴子和用户（已绑定的）
        Post post = (Post) request.getSession().getAttribute("post");
        User user = (User) request.getSession().getAttribute("user");

        // 执行收藏操作
        PostCollected postCollected = postService.changePostCollected(post.getPostId(), user.getUserId());

        System.out.println(postCollected);
        // 判断是新建收藏还是取消收藏，然后返回给前端
        response.setContentType("text/html;charset=utf-8");
        if (postCollected == null) {
            // 为 null 说明该操作为取消收藏
            post.setPostCollected(post.getPostCollected() - 1);
            request.getSession().setAttribute("post", post);
            response.getWriter().write("cancel");
        } else {
            // 否则为新建点赞
            post.setPostCollected(post.getPostCollected() + 1);
            request.getSession().setAttribute("post", post);
            response.getWriter().write("create");
        }
    }

    /**
     * 获取用户收藏的所有贴子的数量
     */
    public void getAllPostCollectedForNumber(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 获取用户 id
        int userId = Integer.parseInt(request.getParameter("userId"));

        // 获取收藏的贴子总数
        int number = postService.getNumberOfAllPostCollected(userId);

        System.out.println("数量为：" + number);
        response.setContentType("text/html;charset=utf-8");
        response.getWriter().write(""+number);
    }

    /**
     * 按页数获取用户收藏的贴子
     */
    public void getAllPostCollectedByPage(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 获取绑定的用户
        int userId = Integer.parseInt(request.getParameter("userId"));

        // 按页数获取贴子
        // 获取开始和结束的下标
        int page = Integer.parseInt(request.getParameter("page"));
        int num = Integer.parseInt(request.getParameter("num"));

        List<Post> postList = postService.findPostCollectedByPage(userId, page, num);

        response.setContentType("text/html;charset=utf-8");
        ObjectMapper mapper = new ObjectMapper();
        response.getWriter().write(mapper.writeValueAsString(postList));
    }

    /**
     * 检查用户是否已经点赞过
     */
    public void isUserLiked(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 获取到当前贴子和用户
        Post post = (Post) request.getSession().getAttribute("post");
        User user = (User) request.getSession().getAttribute("user");

        // 判断用户是否已经点赞过
        boolean isUserLiked = postService.isUserLiked(post.getPostId(), user.getUserId());

        // 返回给前端
        response.getWriter().write("" + isUserLiked);
    }

    /**
     * 检查用户是否已经收藏过
     */
    public void isUserCollected(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 获取到当前贴子和用户
        Post post = (Post) request.getSession().getAttribute("post");
        User user = (User) request.getSession().getAttribute("user");

        // 判断用户是否已经收藏过
        boolean isUserCollected = postService.isUserCollected(post.getPostId(), user.getUserId());

        // 返回给前端
        response.getWriter().write("" + isUserCollected);
    }

    /**
     * 设置贴子的配图
     */
    public void setPostImage(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 设置请求的解析为 utf-8 ，处理文件名称含有中文的
        request.setCharacterEncoding("utf-8");
        response.setCharacterEncoding("utf-8");

        // 判断是否为为多段数据
        if (ServletFileUpload.isMultipartContent(request)) {
            // 获取存放贴子图片的文件夹路径，并判断该文件夹是否存在，不存在则新建一个
            String postImagePath = getServletContext().getRealPath("/post_Image/");
            File postImageFile = new File(postImagePath);

            System.out.println(postImagePath);
            // 如果文件夹不存在并且创建文件夹失败，记录下错误，返回
            if (!postImageFile.exists() && !postImageFile.mkdir()) {
                System.out.println("贴子图片文件夹不存在且创建失败！");
                return;
            }

            try {
                // 获取到文件上传对象
                FileItemFactory fileItemFactory = new DiskFileItemFactory();
                ServletFileUpload servletFileUpload = new ServletFileUpload(fileItemFactory);

                // 获取表单项
                List<FileItem> fileItems = servletFileUpload.parseRequest(request);
                System.out.println("收到的数据：" + fileItems.size());

                // 每一次都上传一张图片进行处理
                FileItem fileItem = fileItems.get(0);

                // 取出数据
                System.out.println("是图片吗？" + !fileItem.isFormField());
                if (!fileItem.isFormField()) {

                    System.out.println("图片名称：" + fileItem.getName());
                    // 获取文件后缀名
                    String suffix = fileItem.getName().substring(fileItem.getName().lastIndexOf("."));

                    // 采用 uuid 命名，防止文件名相同导致覆盖
                    String newPostImage = UUID.randomUUID().toString() + suffix;
                    fileItem.write(new File(postImagePath + newPostImage));

                    response.getWriter().write(newPostImage);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            System.out.println("并没有多段！");
            response.getWriter().write("error");
        }
    }

    /**
     * 删除掉贴子中的图片
     */
    public void deletePostImage(HttpServletRequest request, HttpServletResponse response) throws IOException {
        request.setCharacterEncoding("utf-8");
        response.setCharacterEncoding("utf-8");

        // 获取图片名称
        String imageName = request.getParameter("imageName");

        // 删除图片
        String postImagePath = getServletContext().getRealPath("/post_Image/");
        File oldHead = new File(postImagePath + imageName);
        boolean success = oldHead.delete();

        // 返回信息给前端
        response.getWriter().write("" + success);
    }

    /**
     * 设置贴子回复的图片
     */
    public void setPostCommentImage(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 设置请求的解析为 utf-8 ，处理文件名称含有中文的
        request.setCharacterEncoding("utf-8");
        response.setCharacterEncoding("utf-8");

        // 判断是否为为多段数据
        if (ServletFileUpload.isMultipartContent(request)) {
            // 获取存放贴子图片的文件夹路径，并判断该文件夹是否存在，不存在则新建一个
            String postCommentImagePath = getServletContext().getRealPath("/post_comment_Image/");
            File postCommentImageFile = new File(postCommentImagePath);

            // 如果文件夹不存在并且创建文件夹失败，记录下错误，返回
            if (!postCommentImageFile.exists() && !postCommentImageFile.mkdir()) {
                System.out.println("贴子回复图片文件夹不存在且创建失败！");
                return;
            }

            try {
                // 获取到文件上传对象
                FileItemFactory fileItemFactory = new DiskFileItemFactory();
                ServletFileUpload servletFileUpload = new ServletFileUpload(fileItemFactory);

                // 获取表单项
                List<FileItem> fileItems = servletFileUpload.parseRequest(request);
                System.out.println("收到的数据：" + fileItems.size());

                // 先获取唯一标识
                FileItem identify = fileItems.get(1);

                // 再获取图片，每一次都上传一张图片进行处理
                FileItem fileItem = fileItems.get(0);

                // 取出数据
                System.out.println("是图片吗？" + !fileItem.isFormField());
                if (!fileItem.isFormField()) {

                    System.out.println("图片名称：" + fileItem.getName());
                    // 获取文件后缀名
                    String suffix = fileItem.getName().substring(fileItem.getName().lastIndexOf("."));

                    // 采用 uuid 命名，防止文件名相同导致覆盖
                    String newPostCommentImage = UUID.randomUUID().toString() + suffix;
                    fileItem.write(new File(postCommentImagePath + newPostCommentImage));

                    // 将图片资源映射到数据库中
                    System.out.println(identify.getString());
                    resourceService.createCommentResource(identify.getString(), newPostCommentImage);

                    response.getWriter().write(newPostCommentImage);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else {
            System.out.println("并没有多段！");
            response.getWriter().write("error");
        }
    }

    /**
     * 删除掉贴子回复中的图片
     */
    public void deletePostCommentImage(HttpServletRequest request, HttpServletResponse response) throws IOException {
        request.setCharacterEncoding("utf-8");
        response.setCharacterEncoding("utf-8");

        // 获取图片名称
        String imageName = request.getParameter("imageName");
        System.out.println("要删除的回复图片：" + imageName);

        // 删除图片
        String postImagePath = getServletContext().getRealPath("/post_comment_Image/");
        File oldImage = new File(postImagePath + imageName);
        boolean success = oldImage.delete();

        // 返回信息给前端
        response.getWriter().write("" + success);
    }
}
